//
//  Copyright RevenueCat Inc. All Rights Reserved.
//
//  Licensed under the MIT License (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      https://opensource.org/licenses/MIT
//
//  PromotionalOffer.swift
//
//  Created by Josh Holtz on 1/18/22.

import Foundation
import StoreKit

/// Represents a ``StoreProductDiscount`` that has been validated and
/// is ready to be used for a purchase.
///
/// #### Related Symbols
/// - ``Purchases/promotionalOffer(forProductDiscount:product:)``
/// - ``Purchases/getPromotionalOffer(forProductDiscount:product:completion:)``
/// - ``StoreProduct/eligiblePromotionalOffers()``
/// - ``Purchases/eligiblePromotionalOffers(forProduct:)``
/// - ``Purchases/purchase(package:promotionalOffer:)``
/// - ``Purchases/purchase(package:promotionalOffer:completion:)``
/// - ``Purchases/purchase(product:promotionalOffer:)``
/// - ``Purchases/purchase(product:promotionalOffer:completion:)``
@objc(RCPromotionalOffer)
public final class PromotionalOffer: NSObject {

    /// The ``StoreProductDiscount`` in this offer.
    @objc public let discount: StoreProductDiscount
    /// The ``PromotionalOffer/SignedData-swift.class`` provides information about the ``PromotionalOffer``'s signature.
    @objc public let signedData: SignedData

    init(discount: StoreProductDiscountType, signedData: SignedData) {
        self.discount = StoreProductDiscount.from(discount: discount)
        self.signedData = signedData
    }

}

extension PromotionalOffer: Sendable {}

// MARK: - SignedData

@objc public extension PromotionalOffer {

    /// Contains the details of a promotional offer discount that you want to apply to a payment.
    @objc(RCPromotionalOfferSignedData)
    final class SignedData: NSObject {

        /// The subscription offer identifier.
        @objc public let identifier: String
        /// The key identifier of the subscription key.
        @objc public let keyIdentifier: String
        /// The nonce used in the signature.
        @objc public let nonce: UUID
        /// The cryptographic signature of the offer parameters, generated on RevenueCat's server.
        @objc public let signature: String
        /// The UNIX time, in milliseconds, when the signature was generated.
        @objc public let timestamp: Int

        init(identifier: String, keyIdentifier: String, nonce: UUID, signature: String, timestamp: Int) {
            self.identifier = identifier
            self.keyIdentifier = keyIdentifier
            self.nonce = nonce
            self.signature = signature
            self.timestamp = timestamp
        }

        public override func isEqual(_ object: Any?) -> Bool {
            guard let other = object as? Self else { return false }

            return self == other
        }

        // swiftlint:disable:next missing_docs nsobject_prefer_isequal
        public static func == (
            lhs: PromotionalOffer.SignedData,
            rhs: PromotionalOffer.SignedData
        ) -> Bool {
            return (lhs.identifier == rhs.identifier &&
                    lhs.keyIdentifier == rhs.keyIdentifier &&
                    lhs.nonce == rhs.nonce &&
                    lhs.signature == rhs.signature &&
                    lhs.timestamp == rhs.timestamp)
        }

    }

}

extension PromotionalOffer.SignedData: Sendable {}

extension PromotionalOffer.SignedData {

    enum Error: Swift.Error {

        /// The signature generated by the backend could not be decoded.
        case failedToDecodeSignature(String)

    }

    convenience init(sk1PaymentDiscount discount: SKPaymentDiscount) {
        self.init(identifier: discount.identifier,
                  keyIdentifier: discount.keyIdentifier,
                  nonce: discount.nonce,
                  signature: discount.signature,
                  timestamp: discount.timestamp.intValue)
    }

    var sk1PromotionalOffer: SKPaymentDiscount {
        return SKPaymentDiscount(identifier: self.identifier,
                                 keyIdentifier: self.keyIdentifier,
                                 nonce: self.nonce,
                                 signature: self.signature,
                                 timestamp: self.timestamp as NSNumber)
    }

    @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
    var sk2PurchaseOption: Product.PurchaseOption {
        get throws {
            let signature: Data

            if let decoded = Data(base64Encoded: self.signature) {
                signature = decoded
            } else {
                throw Error.failedToDecodeSignature(self.signature)
            }

            return .promotionalOffer(
                offerID: self.identifier,
                keyID: self.keyIdentifier,
                nonce: self.nonce,
                signature: signature,
                timestamp: self.timestamp
            )
        }
    }

}

// MARK: -

extension PromotionalOffer.SignedData.Error: LocalizedError {

    var errorDescription: String? {
        switch self {
        case let .failedToDecodeSignature(signature):
            return "The signature generated by RevenueCat could not be decoded: \(signature)"
        }
    }

}
